using System;
//using Microsoft.Office.Core;
using Extensibility;
using System.Runtime.InteropServices;
using EnvDTE;
using System.Collections;
using System.IO;
using System.Windows.Forms;

namespace ZipStudio
{
	public class FileItem
	{
		private bool _miscFileItem = false;
		public bool MiscFileItem
		{
			get { return _miscFileItem; }
			set { _miscFileItem = value; }
		}
		private object _item = null;
		public object Item
		{
			get { return _item; }
			set { _item = value; }
		}
		public FileItem(object Item)
		{
			this.Item = Item;
			if(Item is ProjectItem)
			{
				try
				{
					Project owner = ((ProjectItem)Item).ContainingProject;
					if( (owner != null) &&
						(owner.UniqueName == "<MiscFiles>") )
					{
						this.MiscFileItem = true;
					}
				}
				catch {}
			}
		}
	}
	public class Addin
	{
		static private DateTime _startup = DateTime.Now;
		static public DateTime StartupTime
		{
			get { return _startup; }
			set { _startup = value; }
		}
		private _DTE _dte;
		public _DTE DTE
		{
			get { return _dte.DTE; }
		}
		public Addin(_DTE ApplicationObject)
		{
			_dte = ApplicationObject;
		}
		public bool Available()
		{
			if(this.DTE.Solution == null) return false;
			Hashtable files = new Hashtable();
			int numZipFiles = this.GetZipFiles(files);
			return (numZipFiles > 0) ? true : false;
		}
		private bool Execute(Hashtable Files)
		{
			int numFiles = GetZipFiles(Files);
			if(numFiles > 0)
			{
				ZipFormData data = this.GetSolutionData(this.DTE.Solution,
					Files);
				string filePath = data.Value(ZipFormData.DataName.SZPath);
				if( (filePath == null) || (filePath == "") )
				{
					FileInfo fi = new FileInfo(this.DTE.Solution.FullName);
					data.Value(ZipFormData.DataName.SZPath,
						fi.FullName.Replace(fi.Extension, ".zip"));
				}
				ZipForm zipForm = new ZipForm(this.GetFilePaths(Files), 
					data);
				zipForm.ShowDialog();
				bool mustSave = this.SaveSolutionData(this.DTE.Solution, 
					zipForm.Data, data.SaveSettings);
				if(mustSave)
				{
					this.DTE.Solution.SaveAs(this.DTE.Solution.FullName);
				}
				return true;
			}
			else
			{
				DialogResult result = MessageBox.Show(
					"ZipStudio is unable to find any files to zip.\r\n" +
					"Would you like to zip the entire solution?",
					ZipFormData.AppString("Name"), MessageBoxButtons.YesNo,
					MessageBoxIcon.Exclamation);
				if(result == DialogResult.Yes)
				{
					this.GetSolutionFiles(Files, this.DTE.Solution);
					return this.Execute(Files);
				}
			}
			return false;
		}
		public bool Execute()
		{
			Hashtable files = new Hashtable();
			return this.Execute(files);
		}
		private ZipFormData GetSolutionData(Solution Solution, 
			Hashtable Files)
		{
			ZipFormData data = new ZipFormData(Solution.FullName, 
				Addin.StartupTime, Files);
			bool saveSettings = false;
			for(int i = 0; i < data.Count; i++)
			{
				ZipFormData.DataName dataName = 
					(ZipFormData.DataName)i;
				string name = data.Name(dataName);
				if(Solution.Globals.get_VariableExists(name))
				{
					try
					{
						data.Value(dataName, 
							Solution.Globals[name].ToString());
						saveSettings = true;
					}
					catch {}
				}
			}
			data.SaveSettings = saveSettings;
			return data;
		}
		private bool SaveSolutionData(Solution Solution,
			ZipFormData Data, bool SaveData)
		{
			bool mustSave = SaveData;
			for(int i = 0; i < Data.Count; i++)
			{
				ZipFormData.DataName dataName = 
					(ZipFormData.DataName)i;
				string name = Data.Name(dataName);
				string dataValue = null;
				if(SaveData)
				{
					dataValue = Data.Value(dataName);
					Solution.Globals[name] = dataValue;
					Solution.Globals.set_VariablePersists(name, SaveData);
				}
				else
				{
					if(Solution.Globals.get_VariableExists(name))
					{
						mustSave = true;
						Solution.Globals.set_VariablePersists(name, false);
					}
				}
			}
			return mustSave;
		}
		private string[] GetFilePaths(Hashtable Files)
		{
			string[] filePaths = new string[0];
			if(Files.Count > 0)
			{
				filePaths = new string[Files.Count];
				int i = 0;
				foreach(object key in Files.Keys)
				{
					filePaths[i++] = key.ToString();
				}
			}
			return filePaths;
		}
		private int GetSolutionFiles(Hashtable Files, Solution Solution)
		{
			int numFiles = Files.Count;
			this.AddFile(Files, Solution);
			for(int i = 1; i <= Solution.Projects.Count; i++)
			{
				Project project = Solution.Projects.Item(i);
				GetProjectFiles(Files, project);
			}
			return Files.Count - numFiles;
		}
		private int GetProjectFiles(Hashtable Files, Project Project)
		{
			int numFiles = Files.Count;
			this.AddFile(Files, Project);
			if(Project.ProjectItems != null)
			{
				for(int i = 1; i <= Project.ProjectItems.Count; i++)
				{
					ProjectItem item = Project.ProjectItems.Item(i);
					this.AddFile(Files, item);
					if(item.ProjectItems != null)
					{
						GetProjectItemFiles(Files, item.ProjectItems);
					}
					// In case not implemented, cover with a try clause..
					try
					{
						if(item.SubProject != null)
						{
							GetProjectFiles(Files, item.SubProject);
						}
					}
					catch 
					{ 
						// ignore and continue
					}
				}
			}
			return Files.Count - numFiles;
		}
		private int GetProjectItemFiles(Hashtable Files, ProjectItem Item)
		{
			int numFiles = Files.Count;
			this.AddFile(Files, Item);
			if(Item.ProjectItems != null)
			{
				this.GetProjectItemFiles(Files, Item.ProjectItems);
			}
			// In case not implemented, cover with a try clause..
			try
			{
				if(Item.SubProject != null)
				{
					this.GetProjectFiles(Files, Item.SubProject);
				}
			}
			catch 
			{ 
				// ignore and continue
			}
			return Files.Count - numFiles;
		}
		private int GetProjectItemFiles(Hashtable Files, ProjectItems Items)
		{
			int numFiles = Files.Count;
			for(int i = 1; i <= Items.Count; i++)
			{
				ProjectItem item = Items.Item(i);
				this.GetProjectItemFiles(Files, item);
			}			
			return Files.Count - numFiles;
		}
		private bool IsFile(string FilePath)
		{
			FileInfo fi = new FileInfo(FilePath);
			return fi.Exists ? true : false;
		}
		private bool IsFolder(string FolderPath)
		{
			return Directory.Exists(FolderPath) ? true : false;
		}
		private bool CanAddFile(string FilePath)
		{
			return (FilePath != "") && (this.IsFile(FilePath));
		}
		private string GetProjectFullName(Project Project)
		{
			string filePath = Project.FullName;
			int extIndex = filePath.LastIndexOf('.');
			string filePathExt = (extIndex > 0) ? 
				filePath.Substring(extIndex + 1) : "";
			extIndex = Project.UniqueName.LastIndexOf('.');
			string uniqueExt = (extIndex > 0) ? Project.UniqueName.
				Substring(extIndex + 1) : "";
			if(filePathExt != uniqueExt)
			{
				if(filePathExt == "")
					filePath += "." + uniqueExt;
				else
					filePath = filePath.Replace(
						filePathExt, uniqueExt);
			}
			return filePath;
		}
		private string AddFilePath(object Item)
		{
			string filePath = null;
			if(Item is Solution) 
			{
				filePath = ((Solution)Item).FullName;
			}
			else
			{
				if(Item is Project)
				{
					filePath = this.GetProjectFullName((Project)Item);
				}
				else
				{
					if(Item is ProjectItem) 
					{
						filePath = ((ProjectItem)Item).get_FileNames(1);
                        if (string.IsNullOrEmpty(filePath))
                        {
                            filePath = this.GetProjectFullName(((ProjectItem)Item).SubProject);
                        }
					}
				}
			}
			return filePath;
		}
		private int AddFile(Hashtable Files, object Item)
		{
			int numFiles = Files.Count;
			string filePath = this.AddFilePath(Item);
			if(this.CanAddFile(filePath))
			{
				FileItem fileItem = new FileItem(Item);
				Files[filePath] = fileItem;
			}
			return Files.Count - numFiles;
		}
		private int GetZipFiles(Hashtable Files)
		{
			if(this.DTE.SelectedItems.Count > 0)
			{
				for(int i = 1; i <= this.DTE.SelectedItems.Count; i++)
				{
					SelectedItem item = this.DTE.SelectedItems.Item(i);
					if(item.Project == null)
					{
						if(item.ProjectItem == null)
						{
							Solution solution = this.DTE.Solution;
							FileInfo fi = new FileInfo(solution.FullName);
							string name = fi.Name.Replace(fi.Extension, "");
							if(name == item.Name)
							{
								this.GetSolutionFiles(Files, solution);
							}
						}
						else
						{
							this.GetProjectItemFiles(Files, item.ProjectItem);
						}
					}
					else
					{
						if(item.ProjectItem == null)
						{
							this.GetProjectFiles(Files, item.Project);
						}
					}
				}
			}
			return Files.Count;
		}
	}
}
